package com.example.pre.tools;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
import java.net.URL;
import java.net.URLEncoder;
import java.security.GeneralSecurityException;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

/**
 * HTTP 连接工具
 * 
 * @ClassName: HttpsClient.java
 * @Description: 用于 HTTP服务之间通信
 */
public class HttpsClient {
	/** https url */
	private String serverUrl;

	/** http连接 */
	private HttpURLConnection connection;

	/** 连接状态 */
	private boolean status = false;

	/** 请求类型 */
	private Method method;

	/** 响应超时时间 单位秒 */
	private int readTimeOut = 10 * 10000;

	private DataOutputStream dos = null;
	private final String NEWLINE = "\r\n";
	private final String PREFIX = "--";
	private final String BOUNDARY = UUID.randomUUID().toString(); // 随机分隔符

	public HttpsClient(String serverUrl) {
		this.serverUrl = serverUrl;
		this.init();
	}

	public HttpsClient(String serverUrl, Method method) {
		this(serverUrl);
		if (method != null) {
			this.setMethod(method);
		}
	}

	/**
	 * 发送get网络请求
	 */
	public static String get(String url) {
		CloseableHttpClient httpClient = null;
		HttpGet get = new HttpGet(url);
		CloseableHttpResponse response = null;
		try {
			httpClient = HttpClients.createDefault();
			response = httpClient.execute(get);
			int statusCode = response.getStatusLine().getStatusCode();
			if (statusCode == 200) {
				HttpEntity entity = response.getEntity();
				String result = EntityUtils.toString(entity, "UTF-8");
				return result;
			} else {
				return null;
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				response.close();
				httpClient.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return null;
	}

	/**
	 * 执行
	 * 
	 * @throws IOException
	 */
	public Message excute() throws IOException {
		if (!this.status) {
			this.connect();
		}
		if (this.dos != null) {
			dos.writeBytes(PREFIX + BOUNDARY + PREFIX + NEWLINE);
			dos.flush();
			dos.close();
		}
		int code = this.connection.getResponseCode();
		String body = getResponseBody();
		Map<String, List<String>> header = this.connection.getHeaderFields();
		String status = this.connection.getResponseMessage();
		this.connection.disconnect(); // close connection
		return new Message().setResponseCode(code).setHeader(header).setResponseStatus(status).setBody(body);
	}

	/**
	 * 设置参数
	 * 
	 * @param key
	 * @param value
	 * @return
	 */
	public HttpsClient setRequestProperty(String key, String value) {
		this.connection.setRequestProperty(key, value);
		return this;
	}

	/**
	 * 设置headers
	 * 
	 * @param headers
	 * @return
	 */
	public HttpsClient setHeaders(Map<String, Object> headers) {
		if (headers != null && !headers.isEmpty()) {
			for (Map.Entry<String, Object> entry : headers.entrySet()) {
				String key = entry.getKey();
				String value = headers.get(key).toString();
				this.connection.setRequestProperty(key, value);
			}
		}
		return this;
	}

	/**
	 * 设置请求类型
	 * 
	 * @param method
	 * @return
	 */
	public HttpsClient setMethod(Method method) {
		this.method = method;
		try {
			// set request type
			this.connection.setRequestMethod(this.getMethod());
		} catch (ProtocolException e) {
		}
		return this;
	}

	/**
	 * 设置参数
	 * 
	 * @param params 参数列表
	 * @return
	 * @throws IOException
	 */
	public HttpsClient setParams(Map<String, Object> params) throws IOException {
		if (!this.status) {
			this.connection.setRequestProperty("Content-Type",
					"multipart/form-data;boundary=\"" + BOUNDARY + "\";chaset=\"utf-8\"");
			this.connect();
			this.dos = new DataOutputStream(connection.getOutputStream());
		}
		if (params != null && !params.isEmpty()) {
			for (Map.Entry<String, Object> entry : params.entrySet()) {
				String key = entry.getKey();
				String value = params.get(key).toString();
				dos.writeBytes(PREFIX + BOUNDARY + NEWLINE);
				dos.writeBytes("Content-Disposition: form-data; " + "name=\"" + key + "\"" + NEWLINE);
				dos.writeBytes(NEWLINE);
				dos.write(value.getBytes());
				dos.writeBytes(NEWLINE);
			}
		}
		return this;
	}

	/**
	 * 设置参数
	 * 
	 * @param param 参数
	 * @return
	 * @throws IOException
	 */
	public HttpsClient setParam(String key, Object value) throws IOException {
		if (!this.status) {
			this.connection.setRequestProperty("Content-Type",
					"multipart/form-data;boundary=\"" + BOUNDARY + "\";chaset=\"utf-8\"");
			this.connect();
			this.dos = new DataOutputStream(connection.getOutputStream());
		}
		if (key != null && !"".equals(key)) {
			dos.writeBytes(PREFIX + BOUNDARY + NEWLINE);
			dos.writeBytes("Content-Disposition: form-data; " + "name=\"" + key + "\"" + NEWLINE);
			dos.writeBytes(NEWLINE);
			dos.writeBytes(URLEncoder.encode(value.toString(), "utf-8"));
			dos.writeBytes(NEWLINE);
		}
		return this;
	}

	/**
	 * 上传文件
	 * 
	 * @param file 文件
	 * @return
	 * @throws IOException
	 */
	public HttpsClient upFile(File file) throws IOException {
		return this.upFile(file.getName(), new BufferedInputStream(new FileInputStream(file.getPath())));
	}

	/**
	 * 上传文件
	 * 
	 * @param fileName 文件名称
	 * @param bis      文件流
	 * @return
	 * @throws IOException
	 */
	public HttpsClient upFile(String fileName, BufferedInputStream bis) throws IOException {
		if (dos == null) {
			dos = new DataOutputStream(connection.getOutputStream());
		}
		if (bis != null && fileName != null) {
			dos.writeBytes(PREFIX + BOUNDARY + NEWLINE);
			dos.writeBytes("Content-Disposition: form-data; " + "name=\"" + "file" + "\"" + "; filename=\"" + fileName
					+ "\"" + NEWLINE);
			dos.writeBytes(NEWLINE);
			byte[] buffer = new byte[1024 * 1024];
			while ((bis.read(buffer)) != -1) {
				dos.write(buffer);
			}
			dos.writeBytes(NEWLINE);
			dos.flush();
			bis.close(); // 关闭文件流
		}
		return this;
	}

	/**
	 * 为网络请求设置参数 参数传入为null，直接忽略，不添加，并根据url请求接口（返回json），使用raw方式请求
	 * 
	 * @param url        请求路径
	 * @param headers    请求头
	 * @param parameters 参数集合，类型为Map，可以为空
	 * @param method     请求方式
	 * @return
	 * @throws Exception
	 */
	public String setRawParameters(String url, Map<String, Object> headers, Map<String, Object> parameters,
			Method method) throws Exception {
		if (url == null || url.equals("")) {
			return null;
		}
		// 设置头部信息
		if (headers == null) {

			headers = new HashMap<String, Object>();
		}
		headers.put("Content-type", "application/json");
		// 如果没有参数直接封装头部信息并返回
		if (parameters == null) {
			HttpsClient client = new HttpsClient(url);
			client.setHeaders(headers);
			client.setMethod(method);
			return client.excute().getBody();
		}
		// 如果是post请求就将参数放入请求体中
		if ("POST".equals(method.name()) || "PUT".equals(method.name())) {
			HttpsClient client = new HttpsClient(url);
			client.setMethod(method);
			client.setHeaders(headers);
//			client.addRawData(parameters);

			return client.excute().getBody();
		}
		// 其他的请求将参数拼接在头部
		URIBuilder uriBuilder = new URIBuilder(url);
		Set<Entry<String, Object>> entrySet = parameters.entrySet();
		for (Entry<String, Object> entry : entrySet) {
			if (entry.getValue() == null) {
//				uriBuilder.addParameter(entry.getKey(), "");
			} else {
				uriBuilder.addParameter(entry.getKey(), entry.getValue().toString());
			}
		}
		HttpsClient client = new HttpsClient(uriBuilder.build().toString());
		client.setMethod(method);
		client.setHeaders(headers);
		return client.excute().getBody();
	}

	/**
	 * 执行post
	 * 
	 * @param url        请求路径
	 * @param stringJson 传入的json数据
	 * @param headers    头部信息
	 * @param encode     编码（可选）
	 * @return
	 */
	public static Map<String, Object> httpPostRaw(String url, String stringJson, Map<String, String> headers,
			String encode) {
		Map<String, Object> reslut = new HashMap<String, Object>();
		String str = "";
		if (encode == null) {
			encode = "utf-8";
		}
		// HttpClients.createDefault()等价于 HttpClientBuilder.create().build();
		CloseableHttpClient closeableHttpClient = HttpClients.createDefault();
		HttpPost httpost = new HttpPost(url);

		// 设置header
		httpost.setHeader("Content-type", "application/json");
		if (headers != null && headers.size() > 0) {
			for (Map.Entry<String, String> entry : headers.entrySet()) {
				httpost.setHeader(entry.getKey(), entry.getValue());
			}
		}
		// 组织请求参数
		StringEntity stringEntity = new StringEntity(stringJson, encode);
		httpost.setEntity(stringEntity);
		String content = null;
		CloseableHttpResponse httpResponse = null;
		try {
			// 响应信息
			httpResponse = closeableHttpClient.execute(httpost);
			HttpEntity entity = httpResponse.getEntity();
			Header[] headers2 = httpResponse.getHeaders("Set-Cookie");
			reslut.put("Cookies", headers2);
			content = EntityUtils.toString(entity, encode);
			str = content;
			reslut.put("content", str);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (httpResponse != null) {
					httpResponse.close();
				}
				if (closeableHttpClient != null) {
					closeableHttpClient.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		return reslut;
	}

	/**
	 * 设置响应超时时间
	 * 
	 * @param seconds 秒
	 * @return
	 */
	public HttpsClient setReadTimeOut(int seconds) {
		this.readTimeOut = seconds;
		return this;
	}

	public int getReadTimeOut() {
		return this.readTimeOut;
	}

	/**
	 * 获取url输入流
	 * 
	 * @return
	 * @throws IOException
	 */
	public InputStream getInputStream() throws IOException {
		if (!this.status) {
			this.connect();
		}
		try {
			return this.connection.getInputStream();
		} catch (Exception e) {
			e.printStackTrace();
		}
		throw new IOException();
	}

	/**
	 * 打开连接
	 * 
	 * @throws IOException
	 */
	private void connect() throws IOException {
		this.connection.connect(); // open connection
		this.status = true;
	}

	public boolean testConnect() {
		try {
			URL url = new URL(this.serverUrl);
			HttpURLConnection connection = (HttpsURLConnection) url.openConnection();
			connection.connect(); // 尝试建立连接
		} catch (Exception e) {
			return false;
		}
		connection.disconnect();
		return true;
	}

	/**
	 * 初始化连接
	 * 
	 * @throws IOException
	 * @throws GeneralSecurityException
	 * @throws Exception
	 */
	private void init() {
		try {
			if (this.serverUrl.startsWith("https://")) {
				URL url = new URL(this.serverUrl);
				this.connection = (HttpsURLConnection) url.openConnection();
//				((HttpsURLConnection) this.connection).setSSLSocketFactory(this.getSSLFactory());
				((HttpsURLConnection) this.connection).setHostnameVerifier(new HostnameVerifier() {
					public boolean verify(String urlHostName, SSLSession session) {
						return true;
					}
				});
			} else if (this.serverUrl.startsWith("http://")) {
				URL url = new URL(this.serverUrl);
				this.connection = (HttpURLConnection) url.openConnection();
			} else {
				// 不指定协议，默认为http协议
				this.serverUrl = String.format("http://%s", this.serverUrl);
				URL url = new URL(this.serverUrl);
				this.connection = (HttpURLConnection) url.openConnection();
			}
			this.connection.setReadTimeout(this.readTimeOut * 1000);
			this.connection.setDoOutput(true);
			this.connection.setDoInput(true);
			this.connection.setUseCaches(false);
			this.connection.setRequestProperty("Connection", "Keep-Alive");
			this.connection.setRequestProperty("Accept", "*/*");
//			this.connection.setRequestProperty("Accept-Encoding", "gzip, deflate");
			this.connection.setRequestProperty("Cache-Control", "no-cache");
			this.connection.setInstanceFollowRedirects(true);
		} catch (Exception e) {
			throw new RuntimeException("初始化失败", e);
		}
	}

	private String getResponseBody() throws IOException {
		InputStream inputStream;
		if ((inputStream = connection.getErrorStream()) == null) {
			inputStream = connection.getInputStream(); // 尝试获取正确信息
		}
		BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
		StringBuffer buffer = new StringBuffer();
		String line = null;
		while ((line = reader.readLine()) != null) {
			buffer.append(line);
		}
		inputStream.close();
		return buffer.toString();
	}

	private String getMethod() {
		if (this.method == null) {
			return "GET"; // default method GET
		}
		return this.method.name();
	}

	public static void main(String[] args) throws IOException {
		HttpsClient client = new HttpsClient(
				"https://www.tianqiapi.com/api/?appid=16884346&appsecret=Dy2hBKC3&version=v6");
//		HttpsClient client = new HttpsClient("https://restapi.amap.com/v3/weather/weatherInfo?key=c858ce8c630c6805b143efcede964eb7&city=510100");
//		Message message = client.excute();
//		System.out.println(message);
//		JSONObject json = JSONObject.fromObject(message.getBody());
//		@SuppressWarnings("unchecked")
//		Set<String> keySet = json.keySet();
//		int maxSize = 0;
//		for (String key : keySet) {
//			int length = key.length();
//			if (length > maxSize) maxSize = length;
//		}
//		final int size = maxSize;
//		keySet.forEach(key -> System.out.println(StringUtils.rightPad(key, size) + ": " + json.get(key)));
		Message excute = client.excute();
		System.out.println(excute);
	}

	/**
	 * 请求类型
	 * 
	 * @author rain
	 */
	public enum Method {
		GET, HEAD, POST, PUT, PATCH, DELETE;
	}

	/**
	 * https执行后的结果
	 * 
	 * @author rain
	 */
	public class Message {
		/** 请求状态码 */
		private Integer responseCode;

		/** 请求状态 */
		private String responseStatus;

		/** 请求返回 */
		private String body;

		/** 请求头 */
		private Map<String, List<String>> header;

		private Message() {
		}

		/**
		 * 获取请求状态码
		 * 
		 * @return
		 */
		public Integer getResponseCode() {
			return responseCode;
		}

		/**
		 * 获取请求状态
		 * 
		 * @return
		 */
		public String getResponseStatus() {
			return responseStatus;
		}

		/**
		 * 获取请求返回
		 * 
		 * @return
		 */
		public String getBody() {
			return body;
		}

		/**
		 * 获取请求报头
		 * 
		 * @return
		 */
		public Map<String, List<String>> getHeader() {
			return header;
		}

		private Message setResponseCode(Integer responseCode) {
			this.responseCode = responseCode;
			return this;
		}

		private Message setResponseStatus(String responseStatus) {
			this.responseStatus = responseStatus;
			return this;
		}

		private Message setBody(String body) {
			this.body = body;
			return this;
		}

		private Message setHeader(Map<String, List<String>> header) {
			this.header = header;
			return this;
		}

		public String toString() {
			return "---------------print response message----------------\n" + "date\t:\t"
					+ new Timestamp(System.currentTimeMillis()) + "\n" + "code\t:\t" + responseCode + "\n" + "body\t:\t"
					+ body + "\n" + "header\t:\t" + header + "\n"
					+ "-----------------------------------------------------";
		}
	}

}
